package com.wedimob.boxinternetprotection.freebox.remote;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

import android.util.Base64;

import com.google.gson.Gson;
import com.wedimob.boxinternetprotection.freebox.Freebox;
import com.wedimob.html.Form;
import com.wedimob.html.FormField;
import com.wedimob.html.FormFieldType;
import com.wedimob.html.HTMLPage;
import com.wedimob.html.HtmlTools;

public class RemoteManager {

    private final DefaultHttpClient httpClient = new DefaultHttpClient();
    private final HttpContext localContext = new BasicHttpContext();

    private String email;
    private Freebox freebox;
    private String pwd;
    private static final String REMOTE_SERVER_URL = "http://server.elouadi.biz/app/freebox.php";
    private static PublicKey PUBLIC_KEY;

    public RemoteManager(String email, String pwd, Freebox freebox) {
        super();
        this.email = email;
        this.pwd = pwd; // TODO Crypt ??
        this.freebox = freebox;
    }

    static private class ClientResponse {
        public String mac;
        public String tag;
    }

    static private class ServerResponse {
        public List<ClientResponse> clients;
        public Boolean status;
        public String error;
        public double elapsedTime;
    }
    
    private void checkAccess() {
        if (!freebox.isConnected())
            freebox.connect(pwd);

        if (!freebox.isLocallyConnected())
            throw new RuntimeException("Not connected to a freebox network");

        if (!freebox.isRemoteActive())
            throw new RuntimeException("Remote access is not activated");

    }

    public void registerAccount() {
        checkAccess();

        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "register"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        form.addField(new FormField(FormFieldType.HIDDEN, "freeboxurl", freebox
                .getDistantIp()));
        form.addField(new FormField(FormFieldType.HIDDEN, "freeboxpass", pwd));
        form.addField(new FormField(FormFieldType.HIDDEN, "force", "0"));
        form.addField(new FormField(FormFieldType.HIDDEN, "forcelog", "1"));

        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }

    public void unregisterAccount() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action",
                "unregister"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }

    static public String rsaEncrypt(PublicKey pubKey, String data) {

        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] cipherData = cipher.doFinal(data.getBytes("UTF-8"));
            return new String(cipherData, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    static public PublicKey getPublicKey() {
        if (PUBLIC_KEY == null)
            try {
                String key = HtmlTools.get(
                        new URL(REMOTE_SERVER_URL
                                + "?action=publickey&appid=freebox"),
                        new DefaultHttpClient(), new BasicHttpContext()).getBody();

                /*
                 * key = key.replace("-----BEGIN CERTIFICATE-----", ""); key =
                 * key.replace("-----END CERTIFICATE-----", ""); StringBuilder
                 * bd = new StringBuilder();
                 * bd.append("-----BEGIN CERTIFICATE-----"); for(int i = 0; i <
                 * key.length(); i++) { bd.append(key.charAt(i)); if(i % 64 ==
                 * 0) bd.append("\n"); }
                 * bd.append("\n-----END CERTIFICATE-----");
                 */

                System.out.println(key);

                InputStream is = new ByteArrayInputStream(Base64.decode(
                        key.getBytes(), Base64.DEFAULT));
                DataInputStream dis = new DataInputStream(is);
                byte[] keyBytes = new byte[(int) is.available()];
                dis.readFully(keyBytes);
                dis.close();

                X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                PUBLIC_KEY = kf.generatePublic(spec);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        return PUBLIC_KEY;
    }

    public boolean isActive() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "isactive"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
        return serverResponse.status;
    }

    public boolean isSuspended() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "issuspended"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
        return serverResponse.status;
    }    
    
    public void updateMacAliases() {        
        
        checkAccess();

        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "clientlist"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));

        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);

        Map<String, String> aliases = new HashMap<String, String>();
        for(ClientResponse client : serverResponse.clients) {
            aliases.put(client.mac, client.tag != null ? client.tag : client.mac);
        }
        
        freebox.setMacAlias(aliases);
    }

    public void suspend() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "suspend"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }

    public void reactivate() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "reactivate"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }

    public void resetPwd() {
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "askresetpassword"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }

    public void tagMac(String mac, String tag) {       
        checkAccess();
        Form form = new Form(REMOTE_SERVER_URL, null, "post", null);
        form.addField(new FormField(FormFieldType.HIDDEN, "action", "tagmac"));
        form.addField(new FormField(FormFieldType.HIDDEN, "email", email));
        form.addField(new FormField(FormFieldType.PASSWORD, "pass", pwd));
        form.addField(new FormField(FormFieldType.HIDDEN, "mac", mac));
        form.addField(new FormField(FormFieldType.HIDDEN, "tag", tag));

        HTMLPage response = HtmlTools.post(REMOTE_SERVER_URL, form, httpClient,
                localContext);
        Gson gson = new Gson();
        ServerResponse serverResponse = gson.fromJson(response.getBody(),
                ServerResponse.class);
        if (serverResponse.error != null)
            throw new RuntimeException(serverResponse.error);
    }
}
